/** 
 * File-Name:ESTConstructor.java
 *
 * Created on 2011-10-2 下午04:08:37
 * 
 * @author: Neo (neolimeng@gmail.com)
 * Software Engineering Institute, Peking University, China
 * 
 * Copyright (c) 2009, Peking University
 * 
 *
 */
package com.debugassistant.service.est;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.debugassistant.entity.EST;
import com.debugassistant.entity.StackInfo;
import com.debugassistant.util.StringDealer;
import com.debugassistant.util.StringLengthComparator;

/**
 * Description:
 * 
 * @author: Neo (neolimeng@gmail.com) Software Engineering Institute, Peking
 *          University, China
 * @version 1.0 2011-10-2 下午04:08:37
 */
public class ESTConstructor {
	private static Pattern stackInfoPattern;
	static {
		String strPattern = "at\\s+[^\\u0028\\s]+\\s*\\u0028[^\\u0029]+\\u0029";
		stackInfoPattern = Pattern
				.compile(strPattern, Pattern.CASE_INSENSITIVE);
	}

	/**
	 * 
	 * Description:Exctract exception information from an exception stack trace
	 * represented as a String, and use the information to construct
	 * ExceptionInfo objects.
	 * 
	 * @param stackTraceString
	 * @return ExceptionInfo
	 */
	public static EST construct(String stackTraceString) {
		if (stackTraceString == null) {
			return null;
		} else {
			String exceptionName = extractExceptionName(stackTraceString);
			if (exceptionName == null) {
				return null;
			} else {
				EST exceptionInfo = new EST();
				String tip = extractExceptionTip(stackTraceString,
						exceptionName);
				exceptionInfo.setTip(tip);

				exceptionInfo.setFullName(exceptionName);
				exceptionInfo.setStackTraceString(stackTraceString);
				exceptionInfo
						.setStackTrace(extractStackTrace(stackTraceString));

				return exceptionInfo;
			}
		}
	}

	private static String extractExceptionName(String stackTraceString) {
		// return new String(stackTraceString.substring(0, stackTraceString
		// .indexOf("Exception") + 9));
		String name = null;
		stackTraceString = StringDealer.trimEmptyLine(stackTraceString);
		StringReader sr = new StringReader(stackTraceString);
		BufferedReader br = new BufferedReader(sr);
		try {
			String firstLine = br.readLine();
			if (firstLine != null && !firstLine.trim().startsWith("at")) {
				// System.out.println("firstLine:" + firstLine);
				StringTokenizer st = new StringTokenizer(firstLine,
						" \t\r\f():\",';\\[]");
				ArrayList<String> tokenList = new ArrayList<String>();
				while (st.hasMoreTokens()) {
					String thisToken = st.nextToken();
					tokenList.add(thisToken);
				}
				StringLengthComparator slc = new StringLengthComparator();
				Collections.sort(tokenList, slc);
				for (int i = 0; i < tokenList.size(); ++i) {
					String thisToken = tokenList.get(i).toLowerCase();
					if (thisToken.indexOf("exception") >= 0) {
						name = tokenList.get(i);
						break;
					}
				}
			}
			br.close();
			sr.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return name;
	}

	private static String extractExceptionTip(String stackTraceString,
			String exceptionName) {
		String tip = "";
		stackTraceString = StringDealer.trimEmptyLine(stackTraceString);
		StringReader sr = new StringReader(stackTraceString);
		BufferedReader br = new BufferedReader(sr);
		try {
			String firstLine = br.readLine().trim();
			int name_pos = firstLine.indexOf(exceptionName);
			if (name_pos >= 0) {
				int start_pos = firstLine.indexOf(":", name_pos);
				Matcher matcher = stackInfoPattern.matcher(firstLine);
				int max_pos = firstLine.length() - 1;
				if (matcher.find()) {
					max_pos = matcher.start();
				}
				if (start_pos >= 0 && start_pos < max_pos) {
					tip = new String(firstLine.substring(start_pos + 1,
							firstLine.length())).trim();
				}
			}
			br.close();
			sr.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return tip;
	}

	private static List<StackInfo> extractStackTrace(String stackTraceString) {
		ArrayList<StackInfo> stackTrace = new ArrayList<StackInfo>();
		StringReader sr = new StringReader(stackTraceString);
		BufferedReader br = new BufferedReader(sr);
		try {
			String oneLine = br.readLine();
			while (oneLine != null) {
				Matcher matcher = stackInfoPattern.matcher(oneLine);
				while (matcher.find()) {
					String thisUsefulString = new String(oneLine.substring(
							matcher.start() + 2, matcher.end()));
					int left_pos = thisUsefulString.indexOf("(");
					String fullMethodName = new String(thisUsefulString
							.substring(0, left_pos)).trim();
					if (fullMethodName.indexOf(".") < 0) {
						continue;
					}
					String fileAndPos = new String(thisUsefulString.substring(
							left_pos + 1, thisUsefulString.length() - 1))
							.trim();
					// int methodName_pos = fullMethodName.lastIndexOf(".");
					// if (methodName_pos <=0 || methodName_pos >=
					// fullMethodName.length() - 2){
					// continue;
					// }
					// String className = new String(fullMethodName.substring(0,
					// methodName_pos));
					// String methodName = new
					// String(fullMethodName.substring(methodName_pos+1,
					// fullMethodName.length()));
					String fileName = fileAndPos;
					int lineNum = -1;
					StringTokenizer st = new StringTokenizer(fileAndPos, ":");
					if (st.hasMoreTokens()) {
						fileName = st.nextToken();
					}
					if (st.hasMoreTokens()) {
						String thisToken = st.nextToken().trim();
						try {
							lineNum = Integer.parseInt(thisToken);
						} catch (Exception e) {
							e.printStackTrace();
							System.out.println("error int parser:" + thisToken);
						}
					}
					StackInfo thisStackInfo = new StackInfo();
					thisStackInfo.setPosition(stackTrace.size());
					// thisStackInfo.set
					thisStackInfo.setMethodName(fullMethodName);
					thisStackInfo.setFileName(fileName);
					thisStackInfo.setLineNum(lineNum);
					stackTrace.add(thisStackInfo);
				}
				oneLine = br.readLine();
			}
			br.close();
			sr.close();

		} catch (IOException e) {
			e.printStackTrace();
		}

		return stackTrace;
	}

	/**
	 * Description:
	 * 
	 * @param args
	 *            void
	 */
	public static void main(String[] args) {
		String stackTraceString = "java.lang.NullPointerException	at com.rportal.web.action.PaperAction.uploadPaperFile(PaperAction.java:91)	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)	at java.lang.reflect.Method.invoke(Unknown Source)";

		EST exceptionInfo = ESTConstructor.construct(stackTraceString);
		if (null != exceptionInfo) {
			System.out.println("ExceptionName:  " + exceptionInfo.getFullName()
					+ "\t" + exceptionInfo.getShortName() + "\t"
					+ exceptionInfo.getTip());
			List<StackInfo> stackTrace = exceptionInfo.getStackTrace();
			if (stackTrace != null) {
				for (int i = 0; i < stackTrace.size(); i++) {
					StackInfo stackInfo = stackTrace.get(i);
					System.out.println("@ " + stackInfo.getMethodName() + "\t"
							+ stackInfo.getClassName() + "\t"
							+ stackInfo.getShortClassName() + "("
							+ stackInfo.getFileName() + ":"
							+ stackInfo.getLineNum() + ")");
				}
			}
		}
	}
}
